﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.IO;

namespace WpfWordsLearner.Interop
{
	/// <summary>
	/// <example>
	/// using System.IO;
	///String linkDir = "C:\\Documents and Settings";
	///DirectoryInfo directoryInfo = new DirectoryInfo(linkDir);
	///String targetDir = ReparsePoint.GetTargetDir(directoryInfo); 
	///if (targetDir == "")
	///{    // The directory is not a link.}
	///else
	///{    // The directory is a link.}
	/// </example>
	/// </summary>
	internal class Windows7ReparsePoint
	{
		#region "DllImports, Constants & Structs"
		private const Int32 INVALID_HANDLE_VALUE = -1;
		private const Int32 OPEN_EXISTING = 3;
		private const Int32 FILE_FLAG_OPEN_REPARSE_POINT = 0x200000;
		private const Int32 FILE_FLAG_BACKUP_SEMANTICS = 0x2000000;
		private const Int32 FSCTL_GET_REPARSE_POINT = 0x900A8;

		/// <summary>
		/// If the path "REPARSE_GUID_DATA_BUFFER.SubstituteName" 
		/// begins with this prefix,
		/// it is not interpreted by the virtual file system.
		/// </summary>
		private const String NonInterpretedPathPrefix = "\\??\\";

		[StructLayout(LayoutKind.Sequential)]
		private struct REPARSE_GUID_DATA_BUFFER
		{
			public UInt32 ReparseTag;
			public UInt16 ReparseDataLength;
			public UInt16 Reserved;
			public UInt16 SubstituteNameOffset;
			public UInt16 SubstituteNameLength;
			public UInt16 PrintNameOffset;
			public UInt16 PrintNameLength;

			/// <summary>
			/// Contains the SubstituteName and the PrintName.
			/// The SubstituteName is the path of the target directory.
			/// </summary>
			[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x3FF0)]
			public byte[] PathBuffer;
		}

		[DllImport("kernel32.dll", SetLastError = true)]
		private static extern IntPtr CreateFile(String lpFileName,
												Int32 dwDesiredAccess,
												Int32 dwShareMode,
												IntPtr lpSecurityAttributes,
												Int32 dwCreationDisposition,
												Int32 dwFlagsAndAttributes,
												IntPtr hTemplateFile);

		[DllImport("kernel32.dll", SetLastError = true)]
		private static extern Int32 CloseHandle(IntPtr hObject);

		[DllImport("kernel32.dll", SetLastError = true)]
		private static extern Int32 DeviceIoControl(IntPtr hDevice,
													 Int32 dwIoControlCode,
													 IntPtr lpInBuffer,
													 Int32 nInBufferSize,
													 IntPtr lpOutBuffer,
													 Int32 nOutBufferSize,
													 out Int32 lpBytesReturned,
													 IntPtr lpOverlapped);
		#endregion

		/// <summary>
		/// Gets the target directory from a directory link in Windows Vista.
		/// </summary>
		/// <param name="directoryInfo">The directory info of this directory 
		/// link</param>
		/// <returns>the target directory, if it was read, 
		/// otherwise an empty string.</returns>
		public static String GetTargetDir(DirectoryInfo directoryInfo)
		{
			String targetDir = "";

			try
			{
				// Is it a directory link?
				if ((directoryInfo.Attributes
					& FileAttributes.ReparsePoint) != 0)
				{
					// Open the directory link:
					IntPtr hFile = CreateFile(directoryInfo.FullName,
												0, 0,
												IntPtr.Zero,
												OPEN_EXISTING,
												FILE_FLAG_BACKUP_SEMANTICS |
												FILE_FLAG_OPEN_REPARSE_POINT,
												IntPtr.Zero);
					if (hFile.ToInt32() != INVALID_HANDLE_VALUE)
					{
						// Allocate a buffer for the reparse point data:
						Int32 outBufferSize = Marshal.SizeOf
							(typeof(REPARSE_GUID_DATA_BUFFER));
						IntPtr outBuffer = Marshal.AllocHGlobal(outBufferSize);

						try
						{
							// Read the reparse point data:
							Int32 bytesReturned;
							Int32 readOK = DeviceIoControl(hFile,
													   FSCTL_GET_REPARSE_POINT,
															IntPtr.Zero,
															0,
															outBuffer,
															outBufferSize,
															out bytesReturned,
															IntPtr.Zero);
							if (readOK != 0)
							{
								// Get the target directory from the reparse 
								// point data:
								REPARSE_GUID_DATA_BUFFER rgdBuffer =
									(REPARSE_GUID_DATA_BUFFER)
									Marshal.PtrToStructure
								 (outBuffer, typeof(REPARSE_GUID_DATA_BUFFER));
								targetDir = Encoding.Unicode.GetString
										  (rgdBuffer.PathBuffer,
										  rgdBuffer.SubstituteNameOffset,
										  rgdBuffer.SubstituteNameLength);
								if (targetDir.StartsWith
									(NonInterpretedPathPrefix))
								{
									targetDir = targetDir.Substring(NonInterpretedPathPrefix.Length);
								}
							}
						}
						catch (Exception)
						{
						}

						// Free the buffer for the reparse point data:
						Marshal.FreeHGlobal(outBuffer);
						// Close the directory link:
						CloseHandle(hFile);
					}
				}
			}
			catch (Exception)
			{
			}

			return targetDir;
		}
	}

	internal class AeroManager
	{
		public readonly uint DWM_EC_DISABLECOMPOSITION = 0;
		public readonly uint DWM_EC_ENABLECOMPOSITION = 1;

		[StructLayout(LayoutKind.Sequential)]
		public struct MARGINS
		{
			public int Left;
			public int Right;
			public int Top;
			public int Bottom;
		}

		[DllImport("dwmapi.dll", PreserveSig = false)]
		public static extern void DwmExtendFrameIntoClientArea(IntPtr hwnd, ref MARGINS margins);
		[DllImport("dwmapi.dll", EntryPoint = "DwmEnableComposition")]
		protected extern static uint Win32DwmEnableComposition(uint uCompositionAction);
		[DllImport("dwmapi.dll", PreserveSig = false)]
		public static extern bool DwmIsCompositionEnabled();


		public bool ControlAero(bool enable)
		{
			try
			{
				if (enable)
					Win32DwmEnableComposition(DWM_EC_ENABLECOMPOSITION);
				if (!enable)
					Win32DwmEnableComposition(DWM_EC_DISABLECOMPOSITION);

				return true;
			}
			catch { return false; }
		}

		public bool IsAeroEnabled()
		{
			return DwmIsCompositionEnabled();
		}
	}


	/// <summary>
	/// <example>
	/// if (DWM.DwmIsCompositionEnabled())
	///{
	///    Margins = new DWM.MARGINS();
	///    Margins.Top = 50;
	///    Margins.Left = 0;
	///    Margins.Right = 0;
	///    Margins.Bottom = 0;
	///    DWM.DwmExtendFrameIntoClientArea(this.Handle, ref Margins);
	///
	///    DWM.WTA_OPTIONS ops = new DWM.WTA_OPTIONS();
	///    // We Want To Hide the Caption and the Icon
	///    ops.Flags = DWM.WTNCA_NODRAWCAPTION | DWM.WTNCA_NODRAWICON;
	///    // If we set the Mask to the same value as the Flags, the Flags are Added. If Not They are Removed
	///    ops.Mask = DWM.WTNCA_NODRAWCAPTION | DWM.WTNCA_NODRAWICON;
	///    // Set It, The Marshal.Sizeof() stuff is to get the right size of the custom struct, and in UINT/DWORD Form
	///    DWM.SetWindowThemeAttribute(this.Handle,
	///        DWM.WindowThemeAttributeType.WTA_NONCLIENT,
	///        ref ops,
	///        (uint)Marshal.SizeOf(typeof(DWM.WTA_OPTIONS))
	///    );
	///}
	/// </example>
	/// </summary>
	public class DWM
	{
		/// <summary>
		/// How Far On Each Side to Extend the Glass Effect.
		/// </summary>
		[StructLayout(LayoutKind.Sequential)]
		public struct MARGINS
		{
			public int Left;
			public int Right;
			public int Top;
			public int Bottom;
		}

		/// <summary>
		/// Do Not Draw The Caption (Text)
		/// </summary>
		public static uint WTNCA_NODRAWCAPTION = 0x00000001;
		/// <summary>
		/// Do Not Draw the Icon
		/// </summary>
		public static uint WTNCA_NODRAWICON = 0x00000002;
		/// <summary>
		/// Do Not Show the System Menu
		/// </summary>
		public static uint WTNCA_NOSYSMENU = 0x00000004;
		/// <summary>
		/// Do Not Mirror the Question mark Symbol
		/// </summary>
		public static uint WTNCA_NOMIRRORHELP = 0x00000008;

		/// <summary>
		/// The Options of What Attributes to Add/Remove
		/// </summary>
		[StructLayout(LayoutKind.Sequential)]
		public struct WTA_OPTIONS
		{
			public uint Flags;
			public uint Mask;
		}

		/// <summary>
		/// What Type of Attributes? (Only One is Currently Defined)
		/// </summary>
		public enum WindowThemeAttributeType
		{
			WTA_NONCLIENT = 1,
		};

		/// <summary>
		/// Extends the Aero Glass Effect into the Client Area
		/// </summary>
		/// <param name="hWnd">The Window to Get Extended</param>
		/// <param name="Margins">The Margins, Defining How To Extend the Glass Effect</param>
		[DllImport("dwmapi.dll", PreserveSig = false)]
		public static extern void DwmExtendFrameIntoClientArea(IntPtr hWnd, ref MARGINS Margins);

		/// <summary>
		/// Checks to See if Desktop Composition Is Enabled (If Off, Aero Is Off)
		/// </summary>
		/// <returns>True if Desktop Composition is Enabled, False If Not</returns>
		[DllImport("dwmapi.dll", PreserveSig = false)]
		public static extern bool DwmIsCompositionEnabled();

		/// <summary>
		/// Set The Window's Theme Attributes
		/// </summary>
		/// <param name="hWnd">The Handle to the Window</param>
		/// <param name="wtype">What Type of Attributes</param>
		/// <param name="attributes">The Attributes to Add/Remove</param>
		/// <param name="size">The Size of the Attributes Struct</param>
		/// <returns>If The Call Was Successful or Not</returns>
		[DllImport("UxTheme.dll")]
		public static extern int SetWindowThemeAttribute(IntPtr hWnd, WindowThemeAttributeType wtype, ref WTA_OPTIONS attributes, uint size);
	}


}
